bitkeeper revision 1.533 (3f9e6a73y_so5j_vqiY6UPUJof-DFQ)
authorkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Tue, 28 Oct 2003 13:09:07 +0000 (13:09 +0000)
committerkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Tue, 28 Oct 2003 13:09:07 +0000 (13:09 +0000)
time.c:
  Fix up interpolation of time so that, under normal use, we will never see time go backwards.

xen/arch/i386/time.c
xenolinux-2.4.22-sparse/arch/xeno/kernel/time.c

index 395b15b68f0648c3f26417cdffa842c194a2ccb6..ddacbca63d38c2b50baead36a2794e9577ac4661 100644 (file)
@@ -258,9 +258,21 @@ s_time_t get_s_time(void)
 {
     s_time_t now;
     unsigned long flags;
+
     read_lock_irqsave(&xtime_lock, flags);
+
     now = stime_irq + get_time_delta();
+
+    /* Ensure that the returned system time is monotonically increasing. */
+    {
+        static s_time_t prev_now = 0;
+        if ( unlikely(now < prev_now) )
+            now = prev_now;
+        prev_now = now;
+    }
+
     read_unlock_irqrestore(&xtime_lock, flags);
+
     return now; 
 }
 
@@ -335,16 +347,6 @@ int __init init_xeno_time()
     for ( rdtsc_bitshift = 0; cpu_ghz != 0; rdtsc_bitshift++, cpu_ghz >>= 1 )
         continue;
 
-    /*
-     * We actually adjust cpu_freq to be 0.001% slower than the real
-     * frequenecy. This makes time run a little bit slower when interpolating
-     * the passage of time between periodic interrupts, so we expect a little
-     * jump in time whenever an interrupt comes in (roughly 100ns every 10ms).
-     * However, this should avoid us considtently running too fast and jumping
-     * _backwards_ on each interrupt, which would be much worse!
-     */
-    cpu_freq = cpu_freq - (cpu_freq / 100000ULL);
-
     scale  = 1000000000LL << (32 + rdtsc_bitshift);
     scale /= cpu_freq;
     st_scale_f = scale & 0xffffffff;
index 5b31e7fcf546f543c4ee7159adbdd2493be6c2a0..a26238de82e0f392acbf14b1744364a88b34f17c 100644 (file)
@@ -79,11 +79,20 @@ static s64 shadow_system_time;
 static u32 shadow_time_version;
 static struct timeval shadow_tv;
 
+/*
+ * We use this to ensure that gettimeofday() is monotonically increasing. We
+ * only break this guarantee if the wall clock jumps backwards "a long way".
+ */
+static struct timeval last_seen_tv = {0,0};
+
 #ifdef CONFIG_XENO_PRIV
-/* Periodically propagate synchronised time to the RTC and to Xen. */
-static long last_rtc_update, last_xen_update;
+/* Periodically propagate synchronised time base to the RTC and to Xen. */
+static long last_update_to_rtc, last_update_to_xen;
 #endif
 
+/* Periodically take synchronised time base from Xen, if we need it. */
+static long last_update_from_xen;
+
 static u64 processed_system_time;
 
 #define HANDLE_USEC_UNDERFLOW(_tv)         \
@@ -232,11 +241,13 @@ void do_gettimeofday(struct timeval *tv)
 
  again:
     read_lock_irqsave(&xtime_lock, flags);
+
     _tv.tv_usec = get_time_delta_usecs();
     if ( (lost = (jiffies - wall_jiffies)) != 0 )
         _tv.tv_usec += lost * (1000000 / HZ);
     _tv.tv_sec   = xtime.tv_sec;
     _tv.tv_usec += xtime.tv_usec;
+
     if ( unlikely(!TIME_VALUES_UP_TO_DATE) )
     {
         /*
@@ -250,10 +261,18 @@ void do_gettimeofday(struct timeval *tv)
         write_unlock_irqrestore(&xtime_lock, flags);
         goto again;
     }
-    read_unlock_irqrestore(&xtime_lock, flags);
 
     HANDLE_USEC_OVERFLOW(_tv);
 
+    /* Ensure that time-of-day is monotonically increasing. */
+    if ( (_tv.tv_sec < last_seen_tv.tv_sec) ||
+         ((_tv.tv_sec == last_seen_tv.tv_sec) &&
+          (_tv.tv_usec < last_seen_tv.tv_usec)) )
+        _tv = last_seen_tv;
+    last_seen_tv = _tv;
+
+    read_unlock_irqrestore(&xtime_lock, flags);
+
     *tv = _tv;
 }
 
@@ -292,11 +311,15 @@ void do_settimeofday(struct timeval *tv)
     time_maxerror = NTP_PHASE_LIMIT;
     time_esterror = NTP_PHASE_LIMIT;
 
+    /* Reset all our running time counts. They make no sense now. */
+    last_seen_tv.tv_sec = 0;
+    last_update_from_xen = 0;
+
 #ifdef CONFIG_XENO_PRIV
     if ( start_info.dom_id == 0 )
     {
         dom0_op_t op;
-        last_rtc_update = last_xen_update = 0;
+        last_update_to_rtc = last_update_to_xen = 0;
         op.cmd = DOM0_SETTIME;
         op.u.settime.secs        = newtv.tv_sec;
         op.u.settime.usecs       = newtv.tv_usec;
@@ -335,6 +358,7 @@ static inline void do_timer_interrupt(int irq, void *dev_id,
                                       struct pt_regs *regs)
 {
     s64 delta;
+    long sec_diff;
 
     get_time_values_from_xen();
 
@@ -351,21 +375,43 @@ static inline void do_timer_interrupt(int irq, void *dev_id,
         processed_system_time += NS_PER_TICK;
     }
     
-    if ( !independent_wallclock && ((time_status & STA_UNSYNC) != 0) )
+    /*
+     * Take synchronised time from Xen once a minute if we're not
+     * synchronised ourselves, and we haven't chosen to keep an independent
+     * time base.
+     */
+    if ( !independent_wallclock && 
+         ((time_status & STA_UNSYNC) != 0) &&
+         (xtime.tv_sec > (last_update_from_xen + 60)) )
     {
         /* Adjust shadow timeval for jiffies that haven't updated xtime yet. */
         shadow_tv.tv_usec -= (jiffies - wall_jiffies) * (1000000/HZ);
         HANDLE_USEC_UNDERFLOW(shadow_tv);
 
+        /*
+         * Reset our running time counts if they are invalidated by a warp
+         * backwards of more than 500ms.
+         */
+        sec_diff = xtime.tv_sec - shadow_tv.tv_sec;
+        if ( unlikely(abs(sec_diff) > 1) ||
+             unlikely(((sec_diff * 1000000) + 
+                       xtime.tv_usec - shadow_tv.tv_usec) > 500000) )
+        {
+            last_update_to_rtc = last_update_to_xen = 0;
+            last_seen_tv.tv_sec = 0;
+        }
+
         /* Update our unsynchronised xtime appropriately. */
         xtime = shadow_tv;
+
+        last_update_from_xen = xtime.tv_sec;
     }
 
 #ifdef CONFIG_XENO_PRIV
        if ( (start_info.dom_id == 0) && ((time_status & STA_UNSYNC) == 0) )
     {
         /* Send synchronised time to Xen approximately every minute. */
-        if ( xtime.tv_sec > (last_xen_update + 60) )
+        if ( xtime.tv_sec > (last_update_to_xen + 60) )
         {
             dom0_op_t op;
             struct timeval tv = xtime;
@@ -379,7 +425,7 @@ static inline void do_timer_interrupt(int irq, void *dev_id,
             op.u.settime.system_time = shadow_system_time;
             HYPERVISOR_dom0_op(&op);
 
-            last_xen_update = xtime.tv_sec;
+            last_update_to_xen = xtime.tv_sec;
         }
 
         /*
@@ -387,14 +433,14 @@ static inline void do_timer_interrupt(int irq, void *dev_id,
          * clock accordingly every ~11 minutes. Set_rtc_mmss() has to be called
          * as close as possible to 500 ms before the new second starts.
          */
-        if ( (xtime.tv_sec > (last_rtc_update + 660)) &&
+        if ( (xtime.tv_sec > (last_update_to_rtc + 660)) &&
              (xtime.tv_usec >= (500000 - ((unsigned) tick) / 2)) &&
              (xtime.tv_usec <= (500000 + ((unsigned) tick) / 2)) )
         {
             if ( set_rtc_mmss(xtime.tv_sec) == 0 )
-                last_rtc_update = xtime.tv_sec;
+                last_update_to_rtc = xtime.tv_sec;
             else
-                last_rtc_update = xtime.tv_sec - 600;
+                last_update_to_rtc = xtime.tv_sec - 600;
         }
     }
 #endif